/*
 *  Copyright (C) 2010 Glauco
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.balisunrise.common.defaults;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import org.balisunrise.application.Application;
import org.balisunrise.builder.CommonFactory;
import org.balisunrise.builder.FactoryException;
import org.balisunrise.common.DAOProvider;
import org.balisunrise.common.Query;
import org.balisunrise.common.EntityList;
import org.balisunrise.common.EntityLoader;
import org.balisunrise.common.PropertiesReader;
import org.balisunrise.common.abstracts.AbstractBasic;
import org.balisunrise.common.events.AfterLoadEvent;
import org.balisunrise.common.events.BeforeIterateEvent;
import org.balisunrise.common.events.BeforeLoadEvent;
import org.balisunrise.common.listeners.EntityLoadListener;

/**
 * Implementação padrão do EntityLoader
 * TODO: JavaDoc e PropertyChangeSupport
 * @author Glauco
 */
public class DefaultEntityLoader extends AbstractBasic implements EntityLoader{

    private Class entityClass;
    private EntityList entityList;
    private int maxResults;
    private int registrosCarregados;
    private boolean carregouTodosRegistros;
    private Query query;
    private List<EntityLoadListener> listeners;

    public DefaultEntityLoader(Class entityClass) {

        this.entityClass = entityClass;

        String maxR = Application.get().getPropertiesReader()
                .readProperty(PropertiesReader.maxResults);

        this.maxResults = Integer.parseInt(maxR);

        listeners = new ArrayList<EntityLoadListener>();
    }

    public boolean isCarregouTodosRegistros() {
        return carregouTodosRegistros;
    }

    public void setCarregouTodosRegistros(boolean carregouTodosRegistros) {
        this.carregouTodosRegistros = carregouTodosRegistros;
    }

    public Query getQuery() {
        return query;
    }

    public void setQuery(Query query) {
        this.query = query;
    }

    public int getRegistrosCarregados() {
        return registrosCarregados;
    }

    public void setRegistrosCarregados(int registrosCarregados) {
        this.registrosCarregados = registrosCarregados;
    }

    @Override
    public Class getEntityClass() {
        return entityClass;
    }

    @Override
    public void setEntityList(EntityList entityList) {
        this.entityList = entityList;
    }

    @Override
    public EntityList getEntityList() {
        return entityList;
    }

    @Override
    public void setMaxResults(int maxResults) {
        this.maxResults = maxResults;
    }

    @Override
    public int getMaxResults() {
        return maxResults;
    }

    @Override
    public void load(Query query) {

        if (query == null)
            // cria query a ser executada
            createQuery();
        else
            // guarda query a ser executada
            setQuery(query);

        // zera contador de registros
        setRegistrosCarregados(0);

        // BEFORE LOAD EVENT
        try{
            BeforeLoadEvent evt = CommonFactory.getFactory()
                    .createBeforeLoadEvent(this, this.query);
            for (EntityLoadListener ell : listeners) {
                ell.beforeLoad(evt);
            }
            getOwner().actionPerformed(evt);
        }catch(FactoryException ex){
            Application.get().getLogger().log(Level.SEVERE,
                    "EntityLoader não pode construir BeforeLoadEvent", ex);
        }

        // carrega entidades
        List newList = executeQuery(0);

        // verifica se carregou alguma coisa
        if (newList != null)
            // adiciona dados
            getEntityList().setList(newList);
        else
            getEntityList().setList(new ArrayList());

    }

    @Override
    public void iterate() {

        // BEFORE ITERATE EVENT
        try{
            BeforeIterateEvent evt = CommonFactory.getFactory()
                    .createBeforeIterateEvent(this);
            for (EntityLoadListener ell : listeners) {
                ell.beforeIterate(evt);
            }
            getOwner().actionPerformed(evt);
        }catch(FactoryException ex){
            Application.get().getLogger().log(Level.SEVERE,
                    "EntityLoader não pode construir BeforeIterateEvent", ex);
        }

        // carrega entidades
        List newList = executeQuery(getRegistrosCarregados());

        // verifica se carregou alguma coisa
        if (newList != null)
            // adiciona dados
            getEntityList().addList(newList);
        
    }

    private List executeQuery(int initial){

        if (query == null){
            createQuery();
        }

        if (query != null){
            query.setFirstResult(initial);
            query.setMaxResults(getMaxResults());
        }

        // lista de entidades
        List list = null;

        // Pega DAO Provider
        DAOProvider dp = VEAUtil.getDAOProvider(this);

        // executa query
        if (dp != null)
            list = dp.getDAO().list(query);

        // verifica se carregou a lista
        if(list != null){

            // verifica se carregou todos os registros
            if(list.size() == getMaxResults())
                setCarregouTodosRegistros(false);
            else
                setCarregouTodosRegistros(true);

            // soma total de registros carregados
            setRegistrosCarregados(getRegistrosCarregados() + list.size());

        } else {

            // se não carregou nada
            setCarregouTodosRegistros(false);
            setRegistrosCarregados(0);
        }

        //AFTER LOAD EVENT
        try{
            AfterLoadEvent evt = CommonFactory.getFactory()
                    .createAfterLoadEvent(this, list);
            for (EntityLoadListener ell : listeners) {
                ell.afterLoad(evt);
            }
            getOwner().actionPerformed(evt);
        }catch(FactoryException ex){
            Application.get().getLogger().log(Level.SEVERE,
                    "EnitytLoader não pode construir AfterLoadEvent", ex);
        }

        return list;
    }

    private void createQuery() {
        try{
            Query q = Application.get()
                    .getFactory()
                    .createBaliQuery(entityClass);
            q.setOwner(this);
            setQuery(q);
        }catch(FactoryException fex){
            System.out.println(fex.getMessage());
            setQuery(null);
        }
    }

    @Override
    public void addEntityLoadListener(EntityLoadListener listener) {
        listeners.add(listener);
    }

    @Override
    public boolean removeEnityLoadListener(EntityLoadListener listener) {
        return listeners.remove(listener);
    }

    @Override
    public EntityLoadListener[] getEntityLoadListeners() {
        return listeners.toArray(new EntityLoadListener[]{});
    }

}
